package singleprocessscheduling

import (
	"fmt"
	"math/rand"
	"time"

	r "Ryncler/OperatingSystem-Experiment/pkg/util"
)

// 进程结点
type ProcessNode struct {
	Name        string // 进程名称
	ServiceTime int    // 服务时间
	ArriveTime  int    // 到达时间
	Priority    int    // 优先级
	Fcfs        *FCFS
	Sjf         *SJF
	Rr          *RR
	Next        *ProcessNode
}

// 先来先服务
type FCFS struct {
	FinishTime       int     // 完成时间
	TurnaroundTime   int     // 周转时间
	WeigthAroundTime float64 // 带权周转时间
}

// 短作业优先
type SJF struct {
	FinishTime       int     // 完成时间
	TurnaroundTime   int     // 周转时间
	WeigthAroundTime float64 // 带权周转时间
	Flag             int
}

// 时间片轮转
type RR struct {
	FinishTime       int     // 完成时间
	TurnaroundTime   int     // 周转时间
	WeigthAroundTime float64 // 带权周转时间
	FlagTime         int
}

const (
	Fcfs = "fcfs"
	Sjf  = "Sjf"
	Rr   = "Rr"
)

var (
	ProcessNum  = 5
	ProcessHead = ProcessNode{}
)

// 尾插法
func (singleLinkList *ProcessNode) AppendLinkNode(value ProcessNode) {
	// 获取当前头结点
	current := singleLinkList
	// 一直循环获取链表的最后一个结点
	for current.Next != nil {
		current = current.Next
	}
	// 获取到最后一个结点后给它的下一个一个结点赋值
	current.Next = &value
}

// 创建随机进程
func Create() {
	rand.Seed(time.Now().UnixMilli())
	for i := 0; i < ProcessNum; i++ {
		// 初始化进程
		node := &ProcessNode{
			Name:        r.RandomString(4),
			ServiceTime: rand.Intn(10),
			ArriveTime:  i,
			Priority:    rand.Intn(10),
			Fcfs:        &FCFS{},
			Sjf:         &SJF{},
			Rr:          &RR{},
		}
		ProcessHead.AppendLinkNode(*node)
	}
}

// 显示进程信息
func (process *ProcessNode) ShowProcessNode(scheduling string) {
	if process.Next == nil {
		return
	}
	current := process.Next
	for current.Next != nil {
		fmt.Println()
		fmt.Printf("进程名称：%v\n", current.Name)
		fmt.Printf("进程到达时间：%v\n", current.ArriveTime)
		fmt.Printf("进程服务时间：%v\n", current.ServiceTime)
		fmt.Printf("进程优先级：%v\n", current.Priority)

		switch scheduling {
		case Fcfs:
			fmt.Printf("FCFS调度：\n")
			fmt.Printf("完成时间：%v\n", current.Fcfs.FinishTime)
			fmt.Printf("周转时间：%v\n", current.Fcfs.TurnaroundTime)
			fmt.Printf("带权周转时间：%v\n", current.Fcfs.WeigthAroundTime)

		case Sjf:
			fmt.Printf("SJF调度：\n")
			fmt.Printf("完成时间：%v\n", current.Sjf.FinishTime)
			fmt.Printf("周转时间：%v\n", current.Sjf.TurnaroundTime)
			fmt.Printf("带权周转时间：%v\n", current.Sjf.WeigthAroundTime)
		case Rr:
			fmt.Printf("RR调度：\n")
			fmt.Printf("完成时间：%v\n", current.Rr.FinishTime)
			fmt.Printf("周转时间：%v\n", current.Rr.TurnaroundTime)
			fmt.Printf("带权周转时间：%v\n", current.Rr.WeigthAroundTime)
			fmt.Printf("标记状态：%v\n", current.Rr.FlagTime)
		}
		current = current.Next
	}
}

// FCFS调度方法
func (process *ProcessNode) FcfsScheduling() {
	if process.Next == nil {
		return
	}
	current, pre := process.Next, process.Next
	// 将当前结点的完成状态设置为当前进程到达时间
	current.Fcfs.FinishTime = current.ArriveTime
	for current.Next != nil {
		// 如果当前结点的到达时间小于等于前一个结点的到达时间
		if current.ArriveTime <= pre.ArriveTime {
			current.Fcfs.FinishTime = current.ServiceTime + pre.Fcfs.FinishTime
			current.Fcfs.TurnaroundTime = current.Fcfs.FinishTime - current.ArriveTime
			current.Fcfs.WeigthAroundTime = float64(current.Fcfs.TurnaroundTime / current.ServiceTime)
		} else {
			current.Fcfs.FinishTime = current.ServiceTime + current.ArriveTime
			current.Fcfs.TurnaroundTime = current.Fcfs.FinishTime - current.ArriveTime
			current.Fcfs.WeigthAroundTime = float64(current.Fcfs.TurnaroundTime / current.ServiceTime)
		}
		pre = current
		current = current.Next
	}
	process.ShowProcessNode(Fcfs)
}

// SJF调度方法
func (process *ProcessNode) SjfScheduling() {
	if process.Next == nil {
		return
	}
	finishNum := 0
	current, pre, min := process.Next, process.Next, process.Next
	for {
		// 如果完成的进程数等于设置的进程数就退出循环
		if finishNum == ProcessNum {
			break
		}
		// 如果当前结点的下一个结点没有了，重新赋值
		if current.Next == nil {
			current = process.Next
		}

		for {
			if finishNum == ProcessNum {
				break
			}
			// 如果当前结点的SJF调度标记为0
			if current.Sjf.Flag == 0 {
				// 判断当前结点与最小结点的服务时间
				if min.ServiceTime > current.ServiceTime {
					// 记录变更前的结点并变更最小结点
					pre = min
					min = current
				} else {
					// 如果最小结点的SJF标记为1就更改最小结点
					if min.Sjf.Flag == 1 {
						min = current
					} else {
						// 否则计算并标记最小结点为完成
						min.Sjf.FinishTime = min.ServiceTime + pre.Sjf.FinishTime
						if min.Sjf.FinishTime-min.ArriveTime < 0 {
							min.Sjf.TurnaroundTime = 0
						} else {
							min.Sjf.TurnaroundTime = min.Sjf.FinishTime - min.ArriveTime
						}
						if (min.Sjf.TurnaroundTime / min.ServiceTime) < 0 {
							min.Sjf.WeigthAroundTime = 0
						} else {
							min.Sjf.WeigthAroundTime = float64(min.Sjf.TurnaroundTime / min.ServiceTime)
						}
						min.Sjf.Flag = 1
						finishNum++
					}
				}
			}
			if current.Next == nil {
				break
			}
			current = current.Next
		}
	}
	process.ShowProcessNode(Sjf)
}

// RR调度方法
func (process *ProcessNode) RrScheduling(time int) {
	if process.Next == nil {
		return
	}
	// 记录时间
	var currentTime = 0
	current := process.Next
	var processList, readyList, finishList []*ProcessNode
	// 将进程放入进程队列中
	for len(processList) != ProcessNum {
		processList = append(processList, current)
		// 按到达时间放入准备队列
		if current.ArriveTime <= currentTime {
			readyList = append(readyList, current)
		}
		current = current.Next
	}
	for {
		// 如果完成的进程数等于设置的进程数就退出循环
		if len(finishList) == ProcessNum {
			break
		}
		// 如果准备队列不为0
		if len(readyList) != 0 {
			// 获取队列第一个进程
			currentProcess := readyList[0]
			// 去掉队列第一个元素
			readyList = readyList[1:]
			// 判断当前进程的服务时间是否大于时间片
			if currentProcess.ServiceTime > time {
				// 当前时间加时间片时间
				currentTime += time
				// 当前进程服务时间减去时间片时间（没有运行完成的时间）
				currentProcess.ServiceTime -= time
				// 将当前进程放入准备队列末尾
				readyList = append(readyList, currentProcess)
			} else {
				// 将当前时间加为当前进程的服务时间
				currentTime += currentProcess.ServiceTime
				// 当前进程的完成时间为当前时间
				currentProcess.Rr.FinishTime = currentTime
				if currentProcess.Rr.FinishTime-currentProcess.ArriveTime < 0 {
					currentProcess.Rr.TurnaroundTime = 0
				} else {
					currentProcess.Rr.TurnaroundTime = currentProcess.Rr.FinishTime - currentProcess.ArriveTime
				}
				if (currentProcess.Rr.TurnaroundTime / currentProcess.ServiceTime) < 0 {
					currentProcess.Rr.WeigthAroundTime = 0
				} else {
					currentProcess.Rr.WeigthAroundTime = float64(currentProcess.Rr.TurnaroundTime / currentProcess.ServiceTime)
				}
				// 标记为完成
				currentProcess.Rr.FlagTime = 1
				// 将完成的进程放入完成队列
				finishList = append(finishList, currentProcess)
			}
		} else {
			for len(processList) > 0 {
				// 判断当前进程队列第一个元素的到达时间是否小于等于当前时间
				if processList[0].ArriveTime <= currentTime {
					// 如果当前进程的标记为0就添加到准备队列中并去除进程队列第一个元素
					if processList[0].Rr.FlagTime == 0 {
						readyList = append(readyList, processList[0])
						processList = processList[1:]
					} else {
						// 如果是已经完成的进程就跳过
						processList = processList[1:]
					}
				} else {
					// 如果都没有就退出循环
					break
				}
			}
		}
	}
	process.ShowProcessNode(Rr)
}
